/**
 * @file ebex_genetic.h
 *
 * @date Oct 21, 2011
 * @author seth
 *
 * @brief This file is part of EBEX State Estimator, created for the EBEX project
 *
 * This software is copyright (C) 2011 Columbia University
 *
 * EBEX State Estimator is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * EBEX State Estimator is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with EBEX State Estimator; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifndef EBEX_GENETIC_H_
#define EBEX_GENETIC_H_

#include <stdbool.h>
#include <stdint.h>
#include <omp.h>

#include <ebex_util.h>
#include <ebex_quaternion.h>

#include <log_util.h>
#include <memory_util.h>
#include <random_util.h>

typedef struct population_s population_t;
typedef struct organism_s organism_t;

#include <ebex_genetic_mutate.h>
#include <ebex_genetic_mating.h>
#include <ebex_genetic_seed.h>
#include <ebex_genetic_select.h>

/**********************************************************************
 * Callback function typedefs.
 **********************************************************************/

typedef bool (*genetic_iteration_hook_t)(const size_t m_count, population_t *m_pop, organism_t *m_entity);

typedef void (*ebex_userdata_destructor_t)(void *data);
typedef void *(*ebex_userdata_copy_t)(void *data);

typedef bool (*chromosome_constructor_t)(population_t *m_pop, organism_t *m_entity);
typedef void (*chromosome_destructor_t)(population_t *m_pop, organism_t *m_entity);
typedef void (*chromosome_replicate_t)(const population_t *m_pop, organism_t *parent, organism_t *child,
		const int chromosomeid);
typedef unsigned int (*chromosome_to_bytes_t)(const population_t *m_pop, organism_t *m_entity, uint8_t **bytes,
		unsigned int *max_bytes);
typedef void (*chromosome_from_bytes_t)(const population_t *m_pop, organism_t *m_entity, uint8_t *bytes);
typedef char *(*chromosome_to_string_t)(const population_t *m_pop, const organism_t *m_entity, char *text, size_t *textlen);

/*
 * GA operations.
 *
 */
typedef bool 		(*ebex_evaluate_t)(population_t *m_pop, organism_t *m_entity);
typedef bool 		(*ebex_organism_verify_t)(population_t *m_pop, organism_t *m_entity);
typedef bool 		(*ebex_organism_seed_t)(population_t *m_pop, organism_t *m_entity);
typedef organism_t* (*ebex_adapt_t)(population_t *m_pop, organism_t *m_entity);
typedef bool 		(*ebex_select_single_t)(population_t *m_pop, organism_t **m_mother);
typedef bool 		(*ebex_select_pair_t)(population_t *m_pop, organism_t **m_mother, organism_t **m_father);

typedef bool		(*ebex_mutate_allele_t)(population_t *m_pop, organism_t *m_parent, organism_t *m_child, const int m_chromosomeid, const int alleleid);
typedef void 		(*ebex_mate_t)(population_t *m_pop, organism_t *m_mother, organism_t *m_father, organism_t *m_secondchild, organism_t *m_firstchild);
typedef int 		(*ebex_rank_entities_t)(population_t *m_alphapop, organism_t *m_alpha, population_t *m_betapop, organism_t *m_beta);


typedef enum
{
	EVOLUTION_MODE_DARWIN = 0,
	EVOLUTION_MODE_LAMARCK_PARENTS = 1,
	EVOLUTION_MODE_LAMARCK_CHILDREN = 2,
	EVOLUTION_MODE_LAMARCK_ALL = 3,
	EVOLUTION_MODE_BALDWIN_PARENTS = 4,
	EVOLUTION_MODE_BALDWIN_CHILDREN = 8,
	EVOLUTION_MODE_BALDWIN_ALL = 12
} evolution_mode_t;

typedef enum
{
	GENETIC_SELECTION_NONE = 0,
	GENETIC_SELECTION_PARENTS_SURVIVE,
	GENETIC_SELECTION_ONE_PARENT_SURVIVES,
	GENETIC_SELECTION_PARENTS_DIE,
	GENETIC_SELECTION_RESCORE_PARENTS
} genetic_selection_mode_t;

typedef enum
{
	DIFF_EVO_STRATEGY_NONE = 0,
	DIFF_EVO_STRATEGY_BEST,
	DIFF_EVO_STRATEGY_RANDOM,
	DIFF_EVO_STRATEGY_RANDOMTOBEST
} diff_evo_strategy_t;

typedef enum
{
	DIFF_EVO_CROSSOVER_NONE = 0,
	DIFF_EVO_CROSSOVER_BINOMIAL,
	DIFF_EVO_CROSSOVER_EXPONENTIAL
} diff_evo_crossover_t;

typedef enum
{
	DIFF_EVO_REFERENCE_NONE = 0,
	DIFF_EVO_REFERENCE_MEAN,
	DIFF_EVO_REFERENCE_BEST,
	DIFF_EVO_REFERENCE_RANDOM
} diff_evo_reference_t;

typedef enum
{
	GENETIC_SEED_NONE = 0,
	GENETIC_SEED_UNIFORM,
	GENETIC_SEED_GAUSSIAN,
	GENETIC_SEED_SAMPLE,
	GENETIC_SEED_ZERO
} e_genetic_seed_strategy;

typedef enum
{
	HILL_CLIMB_MODE_NONE = 0,
	HILL_CLIMB_MODE_SYSTEMATIC,
	HILL_CLIMB_MODE_RANDOM,
} hill_climb_type_t;


/// Fitness numbers must be positive to be valid
#define GENETIC_MIN_FITNESS			0.0

/// Default values if not given specifically in the configuration
#define GENETIC_DEFAULT_CROSSOVER_RATIO			0.9
#define GENETIC_DEFAULT_MUTATION_RATIO			0.2
#define GENETIC_DEFAULT_ALLELE_MUTATION_RATIO	0.1

/**
 * Chromosome types available
 */
typedef enum
{
	CHROMOSOME_TYPE_NONE,		//!< CHROMOSOME_TYPE_NONE
	CHROMOSOME_TYPE_DOUBLE,		//!< CHROMOSOME_TYPE_DOUBLE
	CHROMOSOME_TYPE_BITSTRING,	//!< CHROMOSOME_TYPE_BITSTRING
	CHROMOSOME_TYPE_QUATERNION, //!< CHROMOSOME_TYPE_QUATERNION
} chromosome_type_t;

/**
 * Common Chromosome structure definition
 */
typedef struct
{
	chromosome_type_t	type;
	size_t				length;
	union
	{
		void 			*vd_allele;
		ebex_quat_t		*q_allele;
		double 			*dbl_allele;
	};
	/*
	 * Mutation/Selection Parameters
	 */
	double 				allele_mutation_prob;
	double 				allele_min_double;
	double 				allele_max_double;
	void				*user_data;
	union
	{
		random_sample_t 	rnd;
		random_sample_3d_t	rnd3d;
	};
} chromosome_t;

/// Organism structure
typedef struct organism_s
{
	double 			fitness; 			//!< fitness Fitness score
	double			previous_weight;	//!< previous_weight Normalized cumulative weighting from previous n steps

	size_t			allocated_chromosomes; //!< allocated_chromosomes Number of chromosomes allocated in organism
	chromosome_t	*chromosome; 		//!< chromosome Pointer to the chromosomes (the genotype).
	size_t 			allele_size;		//!< allele_size Size of all allele data in bytes
	void			*allele_data;		//!< allele_data Pointer to the raw allele data
	void			*data; 				//!< data User defined data containing additional information on the entity

} organism_t;

typedef struct
{
	diff_evo_strategy_t 		strategy; 		//!< strategy Selection strategy for vectors
	diff_evo_crossover_t 		crossover_type; //!< crossover_type Method of mixing vector alleles (binomial or exponential)
	size_t 						num_dv; 		//!< num_dv Number of vectors to use for crossing
	double 						crossover_ratio;//!< crossover_ratio Ratio of alleles to select for mixing
	double 						min_weight;	 	//!< min_weight Lower bound on weight factor
	double 						max_weight;	 	//!< max_weight Upper bound on weight factor (set equal to min_weight to disable dithering)
	double						opposition_rate;//!< opposition_rate Percentage of time to generate an 'opposite' generation for evaluation
	diff_evo_reference_t		reference;		//!< reference Determines what type of organism to use for an opposition reference (best or mean)
	genetic_iteration_hook_t 	iteration_hook;	//!< iteration_hook User function to process prior to each iteration

	bool						scatter;
	size_t						scatter_chromosome;
	double						scatter_fittness;
} diff_evo_t;

typedef struct
{
	hill_climb_type_t			type;			//!< type Type of hill climbing to perform
	ebex_mutate_allele_t 		mutate_allele; 	//!< mutate_allele Allele mutation function callback
	genetic_iteration_hook_t 	iteration_hook;	//!< iteration_hook User function to process prior to each iteration
} hill_climb_t;

typedef struct
{
	double mean, stddev, sum; 		//!< Fitness statistics.
	double current_expval; 			//!< Total of expectancy values.
	double minval; 					//!< Worst fitness value.
	double step; 					//!< Distance between each pointer.
	double offset1, offset2; 		//!< Current pointer offsets.
	int marker; 					//!< The roulette wheel marker.
	int num_to_select; 				//!< Number of organisms to select.
	int current1, current2; 		//!< Currently selected organisms.
	int *permutation; 				//!< Randomly ordered indices.
} genetic_selection_data_t;


/*
 * Population Structure.
 *
 */
typedef struct population_s
{
	size_t max_size;
	size_t stable_size; 		//!< Target population size
	size_t size; 				//!< Current population size
	size_t orig_size; 			//!< Number of organisms at the end of the previous generation
	size_t generation;

	int free_index;

	memchunk_t *organism_memory; 	/* Buffer for entity structures. */
	organism_t **raw_organisms; 	/* The population in id order. */
	organism_t **sorted_organisms; 	/* The population sorted by fitness. */

	size_t num_chromosomes; //!< num_chromosome Number of chromosomes per organism

	void	*data; 				/* User data. */

	size_t						select_state; 			/* Available to selection algorithms. */
	genetic_selection_data_t 	selectdata; /* State values for built-in selection operators. */

	/*
	 * Evolutionary parameters.
	 */
	double crossover_ratio; 				/* Chance for crossover. */
	double mutation_ratio; 					/* Chance for entity mutation. */
	evolution_mode_t scheme; 					/* Evolutionary scheme. */
	genetic_selection_mode_t elitism; 				/* Elitism mode. */


	diff_evo_t 		*de_params; 					/* Parameters for differential evolution. */
	hill_climb_t 	*hc_params;

	e_genetic_mating_type	mating_type;
	e_genetic_mutation_type	mutation_type;
	e_genetic_select_type select_one_type;
	e_genetic_select_type select_two_type;
	e_genetic_seed_strategy seed_strategy;

	/*
	 * The scoring function and the other callbacks are defined here.
	 */
	genetic_iteration_hook_t pre_generation_hook;
	genetic_iteration_hook_t post_generation_hook;

	ebex_userdata_destructor_t organism_data_destructor;
	ebex_userdata_destructor_t organism_data_copy;

	ebex_userdata_destructor_t chromosome_data_destructor;
	ebex_userdata_copy_t chromosome_data_copy;

	ebex_userdata_destructor_t population_data_destructor;
	ebex_userdata_copy_t population_data_copy;

	chromosome_constructor_t chromosome_constructor;
	chromosome_destructor_t chromosome_destructor;
	chromosome_replicate_t chromosome_replicate;
	chromosome_to_bytes_t chromosome_to_bytes;
	chromosome_from_bytes_t chromosome_from_bytes;
	chromosome_to_string_t chromosome_to_string;

	ebex_evaluate_t evaluate;
	ebex_organism_verify_t verify;
	ebex_adapt_t adapt;
	ebex_rank_entities_t rank;
	ebex_organism_seed_t seed;

	/*
	 * Memory handling.
	 */
	memchunk_t *chromoarray_chunk;
	memchunk_t *chromo_chunk;

	/*
	 * Execution locks.
	 */
	omp_lock_t(lock);
	omp_lock_t(chromo_chunk_lock);
} population_t;

/**
 * Function Prototypes
 */
population_t *genetic_pop_new(const int stable_size, const int num_chromosome);
bool genetic_organism_seed(population_t *m_pop, organism_t *adam);
bool genetic_fill_pop(population_t *m_pop, int num);
double genetic_organism_evaluate(population_t *m_pop, organism_t *this_entity);
int genetic_organism_get_rank(population_t *m_pop, organism_t *e);
int genetic_get_index(population_t *m_pop, organism_t *e);
organism_t *genetic_get_organism(population_t *m_pop, const size_t id);
organism_t *genetic_get_ranked_organism(population_t *m_pop, const size_t rank);

bool genetic_free_rank(population_t *m_pop, int m_rank);
bool genetic_free_index(population_t *m_pop, int m_id);
bool genetic_free_organism(population_t *m_pop, organism_t *dying);
void genetic_clear_organism(population_t *p, organism_t *this_entity);
organism_t *genetic_get_new_organism(population_t *m_pop);
organism_t *ebex_genetic_get_mean_organism(population_t *m_pop);

bool ebex_genetic_copy_data(population_t *m_pop, organism_t *dest, organism_t *src, const int chromosome);
size_t ebex_genetic_copy_chromosome(population_t *m_pop, organism_t *dest, organism_t *src, const size_t m_chromosome);
bool genetic_organism_copy_all_chromosomes(population_t *m_pop, organism_t *dest, organism_t *src);
bool genetic_organism_copy(population_t *m_pop, organism_t *dest, organism_t *src);
organism_t *genetic_organism_clone(population_t *m_pop, organism_t *parent);
void genetic_pop_set_parameters(population_t *m_pop, const evolution_mode_t scheme, const genetic_selection_mode_t elitism,
		const double crossover, const double mutation, const double migration);
void genetic_organism_set_allele_mutation_prob(organism_t *m_entity, const int m_num_chrom, const double m_prob);
void genetic_pop_set_allele_min_double(organism_t *m_entity, const int m_num_chrom, const double m_min);
void genetic_organism_set_allele_max_double(organism_t *m_entity, const int m_num_chrom, const double m_max);
bool genetic_genocide(population_t *m_pop, size_t target_size);
bool genetic_genocide_by_fitness(population_t *m_pop, double m_minfitness);
bool genetic_close(population_t *m_pop);
void ebex_genetic_init(void);


#endif /* EBEX_GENETIC_H_ */
